package com.qyy.www_chatroom.component;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qyy.www_chatroom.mapper.MessageMapper;
import com.qyy.www_chatroom.mapper.MessageSessionMapper;
import com.qyy.www_chatroom.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.io.IOException;
import java.util.List;

@Component
public class WebSocketAPI extends TextWebSocketHandler {

    @Autowired
    private OnlineUserManager onlineUserManager;

    //先有一个json对象。这是属于Spring内置的。
    private ObjectMapper objectMapper = new ObjectMapper();

    @Autowired
    private MessageSessionMapper messageSessionMapper;

    @Autowired
    private MessageMapper messageMapper;

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        //这个方法会在websocket连接建立后被自动调用
        System.out.println("【WebSocket】 连接成功");
        //正因为前面加上了拦截器，就可以让每一个往HttpSession中加入的Attribute
        //在WebSocketSession中也被加入一份
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            return;
        }
        System.out.println("获取到的userId-----------------" +user.getUserId());

        //获取到userId之后，就把键值对存起来
        onlineUserManager.online((user.getUserId()),session);

    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        //这个方法是在websocket收到消息后，被自动调用
        System.out.println("【WebSocket】 收到消息！"+message.toString());
        //session会话里面记录了通信双方是谁。Session中就持有了websocket的通信连接
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            System.out.println("[WebsocketAPI] user == null 未登录用户，无法进行消息转发");
            return;
        }
        //2.针对请求进行解析,把json格式的字符串，转成一个java中的对象
        //message.getPayload()得到荷载信息这是json格式，我们把他转成java对象
        MessageRequest req = objectMapper.readValue(message.getPayload(), MessageRequest.class);
        if(req.getType().equals("message")){
            //就进行转发
            transferMessage(user,req);
        }else {
            System.out.println("[WebsocketAPI] req.type 有误" + message.getPayload());
        }
        //后续主要实现这个方法
        //处理消息接收，转发，以及保存消息记录
    }

    //通过这个方法来完成实际的消息转发操作。
    //1.先构造一个待转发的响应对象，MessageResponse
    //2.根据请求的SessionId，获取这个MessageSession的里面都有哪些用户
    //通过查询数据库就知道了
    //3.循环遍历上述这个列表，给列表中的每一个用户都发一份响应消息
    //知道了每个用户的userId,进一步查询刚才准备好的OnlineManager,就知道了对应的WebSocketSession
    //注：除了给好友发，我们还要给自己发。
    //一个会话可能有很多人，群聊前端写起来比较麻烦，不过我们后端API都支持群聊
    //4.转发的消息要记录到数据库中，这样后续用户如果下线之后，重新上线，还能通过历史消息拿到之前的消息
    private void transferMessage(User fromUser, MessageRequest req) throws IOException {
        //考虑一下，如果发消息时候，对方不在线，该怎么处理。给李四发消息，如果不在线。第二步能获取到李四，但是第三步WebSocketSession
        //找不到。不过我们存到数据库中，如果哪天上线了，就能通过历史消息看到张三发来的消息
        // 如果在线，就要立即收到消息。

        //1.先构造一个待转发的响应对象，MessageResponse
        MessageResponse response = new MessageResponse();
        response.setType("message");//不设置也行，因为已经就地初始化了
        response.setFromId(fromUser.getUserId());
        response.setFromName(fromUser.getUsername());
        response.setSessionId(req.getSessionId());
        response.setContent(req.getContent());
        //还需要把java对象转json
        String responseJson = objectMapper.writeValueAsString(response);
        System.out.println("transferMessage respJson:" +responseJson);
        //2.根据请求的SessionId，获取这个MessageSession的里面都有哪些用户
        //通过查询数据库就知道了

        List<Friend> friends = messageSessionMapper.getFriendsBySessionId(req.getSessionId(), fromUser.getUserId());
        //注：数据库操作会把自身用户排除掉。而发消息要给自己这边也发一次，因此把当前用户也要添加进去
        Friend myself = new Friend();
        myself.setFriendId(fromUser.getUserId());
        myself.setFriendName(fromUser.getUsername());
        friends.add(myself);
        //3.循环遍历上述这个列表，给列表中的每一个用户都发一份响应消息
        //知道了每个用户的userId,进一步查询刚才准备好的OnlineManager,就知道了对应的WebSocketSession
        //注：除了给好友发，我们还要给自己发。
        for(Friend friend : friends){
            WebSocketSession webSocketsession = onlineUserManager.getSession(friend.getFriendId());
            if(webSocketsession == null){
                //如果用户不在线，就不发送
                continue;
            }
            webSocketsession.sendMessage(new TextMessage(responseJson));
        }
        //4.转发的消息，还需要放到数据库中，后续用户如果下线，重新上线还可以通过历史消息方式拿到之前的消息
        //需要往message表中写入记录
        Message message = new Message();
        message.setFromId(fromUser.getUserId());
        message.setSessionId(req.getSessionId());
        message.setContent(req.getContent());
        //自增主键和时间属性都可以让SQL、在数据库中生成
        messageMapper.add(message);

    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        //这个方法在连接出现异常后被自动调用
        //如果多开，就把后一个下线。
        System.out.println("【WebSocket】 连接异常" + exception.toString());

        User user = (User) session.getAttributes().get("user");
        if(user == null){
            return;
        }
        onlineUserManager.offline(user.getUserId(), session);

    }
//当前不管是正常断开，还是异常断开，都是要下线的。
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        //连接正常关闭后，被自动调用
        User user = (User) session.getAttributes().get("user");
        if(user == null){
            return;
        }
        onlineUserManager.offline(user.getUserId(), session);
        System.out.println("【WebSocket】 连接关闭!" +status.toString());
    }
}
